package gameplay;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;


public class Grid {
  
  private int[][] grid;
  private int gridWidth;
  private int gridHeight;
  private Point startingPoint;
  private ArrayList<Point> allPoints = new ArrayList<Point>();
  private ArrayList<PathGroup> pathGroupList =  new ArrayList<PathGroup>();
  

  //                     0  1  2  3  4  5  6  7   8   9   10  11  12  13  14  15  16  17   18  19   20
  public static int[] scores = {0, 0, 1, 3, 5, 7, 9, 11, 15, 20, 25, 30, 35, 40, 50, 60, 70, 85, 100, 150, 300};

  public Grid(String filePath) {
    fileToGrid(filePath);
  }
  
  /**
   * 
   * @param fileName File which contain the grid.
   */
  public  void fileToGrid(String fileName) {
    BufferedReader br = null;
    setGridProperties(fileName);
    grid = new int[gridHeight][gridWidth];
    try {
      br = new BufferedReader(new FileReader(fileName));
    } catch (FileNotFoundException e1) {
      e1.printStackTrace();
    }
    String line;
    int lineNumber = 0;
    try {
      while ((line = br.readLine()) != null) {
        for (int i = 0; i < gridWidth; i++) {
          if (i >= line.length() || line.charAt(i) == ' ') {
            grid[lineNumber][i] = -1;
          } else {
        	allPoints.add(new Point(i, lineNumber));
        	if (line.charAt(i) == 'x') {
              grid[lineNumber][i] = 0;
            } else if (line.charAt(i) == 's') {
              startingPoint = new Point(i, lineNumber);
              grid[lineNumber][i] = 0;
              
            } else {
              grid[lineNumber][i] = line.charAt(i);
            } 
          }
        }
        lineNumber++;
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  /**
   * Whether the grid is filled with number.
   * @return
   */
  public boolean isGridFilled() {
	Iterator<Point> allPointsIterator = allPoints.iterator();
	while (allPointsIterator.hasNext()) {
	  if (Util.pointToGrid(grid, allPointsIterator.next()) == 0 ) {
		  return false;
	  }
	} 
	return true;
  }
  
  
  
  
  
  /**
   * Gets the grid properties from a text file.
   * @param fileName FileName containning the grid.
   */
  public void setGridProperties(String fileName) {
    gridWidth = 0;
    gridHeight = 0;
    BufferedReader br = null;
    try {
      br = new BufferedReader(new FileReader(fileName));
    } catch (FileNotFoundException e1) {
      e1.printStackTrace();
    }
        String line;
        try {
      while((line = br.readLine()) != null) {
        gridWidth = Math.max(gridWidth, line.length());
        gridHeight++;
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  /**
   * Prints the grid.
   */
  public void printGrid() {
    if (!Game.print) {
      return ;
    }
    System.out.print("00| ");
    for (int i = 0; i < grid[0].length; i++) {
      System.out.print(String.format("%02d", i) + " "); 
    }
    System.out.print("\n--|"); 
    for (int i = 1; i < grid[0].length ; i++) {
      System.out.print("---"); 
    }
    System.out.println(""); 

    for (int i = 0; i < grid.length; i++) {
      System.out.print((char) (+ i + 65) + " | ");
      for (int j = 0; j < grid[0].length; j++) {
        if (grid[i][j] == -1) {
          System.out.print("   ");
        } else if (grid[i][j] == 0) {
          System.out.print(".. ");
        } else {
          System.out.print(String.format("%02d", grid[i][j]) + " ");
        }
      }
      System.out.println(""); 
    }
    for (int i = 1; i < grid[0].length ; i++) {
      System.out.print("---"); 
    }
    System.out.println("");
    System.out.print("00| ");
    for (int i = 0; i < grid[0].length; i++) {
      System.out.print(String.format("%02d", i) + " "); 
    }
    System.out.println("");
  }

  
  /**
   * Fills the grid zith random number
   */
  public void fillRandomly() {
    for (int i = 0; i < grid.length; i++) {
      for (int j = 0; j < grid[0].length; j++) {
        if (grid[i][j] == 0) {
          grid[i][j] = Util.getRandomNumber(10);
        }
      }
    }
  }

  
  /**
   * Whether a chip can be place on this point.
   * @param point
   * @return
   */
  boolean isValidPoint(Point point) {
    if (point.getX() < 0 ||
    	point.getX() >= this.gridWidth ||
        point.getY() > this.gridHeight ||
        point.getY() < 0  || getValueOfPointInGrid(point) != 0) {
    	System.out.println("Bad Place!"); 
      return false;
    } else {
      return true;
    }
  }
  
  
  /**
   * Launch the search for the score.
   * @return
   */
  public int findScore() {
    pathGroupList = new ArrayList<PathGroup>();
    findPathGroups();
    Iterator<PathGroup> pathGroupListIterator = pathGroupList.iterator();
    int score = 0;
    while (pathGroupListIterator.hasNext()) {
       PathGroup currentScore = pathGroupListIterator.next();
       score = Math.max(score, currentScore.getTotalScore()); 
    }
    return score;
  }
  
  
  /**
   * 
   */
  public void findPathGroups() {
    fillScoreList(startingPoint, startingPoint,
        buildNewPathGroup(), 0);
  }
  
  /**
   * 
   * @return new pathGroup
   */
  public PathGroup buildNewPathGroup() {
    PathGroup pathGroup = new PathGroup();
    pathGroupList.add(pathGroup);
    return pathGroup;
  }
  
  /**
   * 
   * @return
   */
  private PathGroup buildClonePathGroup(PathGroup pathGroup) {
    PathGroup newPathGroup = new PathGroup(pathGroup);
    pathGroupList.add(newPathGroup);
    return newPathGroup;
  }
  
  
  /**
   * Recursive method for the score.
   * @param currentPoint
   * @param previousPoint
   * @param score
   * @throws CloneNotSupportedException
   */
  public void fillScoreList(Point currentPoint, Point previousPoint,
      PathGroup pathGroup, int currentChain) {
    if (currentChain >= pathGroup.getListOfPathSize()) {
      pathGroup.addToList(new ArrayList<Point>());
    }
    // System.out.print("-" + grid[currentPoint.getY()][currentPoint.getX()]);
    if (pathGroup.isPointVisited(currentPoint)) {
      return;
    }
    pathGroup.addVisitedPoint(currentPoint);
    pathGroup.addToList(currentChain,
        new Point(currentPoint.getX(), currentPoint.getY(),
            grid[currentPoint.getY()][currentPoint.getX()]));

    
    boolean hasMoveToNext =
        movePoints(previousPoint, currentPoint, pathGroup, currentChain);
    
    if (!hasMoveToNext) {
//      if (scoreMax.getScore() < score.getScore()) {
//        scoreMax = (Score) score.clone();
//      }
    }
  }
  

  
  
  /**
   * 
   * @param previousPoint
   * @param currentPoint
   * @param score
   * @return
   * @throws CloneNotSupportedException
   */
  public boolean movePoints(Point previousPoint, Point currentPoint,
      PathGroup pathGroup, int currentChain) {
    int currentX = currentPoint.getX();
    int currentY = currentPoint.getY();
    Point[] nextPoints = {
        new Point(currentX, currentY - 1),
        new Point(currentX, currentY + 1),
        new Point(currentX - 1, currentY),
        new Point(currentX + 1, currentY)
    };
    ArrayList<Point> nextPossiblePoints = new ArrayList<Point>();
    ArrayList<PathGroup> nextPossiblePathGroup = new ArrayList<PathGroup>(); 
    for (int i = 0; i < nextPoints.length; i++) {
      if (isPossiblePoint(nextPoints[i], previousPoint, pathGroup)) {
        nextPossiblePoints.add(nextPoints[i]);
        nextPossiblePathGroup.add(nextPossiblePoints.size() == 1  ? pathGroup : buildClonePathGroup(pathGroup));
      }
    }
    boolean isNode = nextPossiblePoints.size() > 1;
    if (nextPossiblePoints.size() == 0 ) {
      return false;
    }

    if (!isNode) {
      fillScoreList(nextPossiblePoints.get(0),
          currentPoint,
          nextPossiblePathGroup.get(0),
          currentChain);
    } else {
      
      
      for (int i = 0; i < nextPossiblePoints.size(); i++) {
        
        for (int point = 0; point < nextPossiblePoints.size(); point++) {
          int possiblePathGroup = (i + point) % nextPossiblePoints.size();
          fillScoreList(nextPossiblePoints.get(point),
            currentPoint,
            nextPossiblePathGroup.get(possiblePathGroup),
            i == 0 ? currentChain : nextPossiblePathGroup.get(possiblePathGroup).getListOfPathSize());
        }
      }
      
    }
      
    return true;
  }
  

  /**
   * 
   * @param nextPoint
   * @param previousPoint
   * @return
   */
  private boolean isPossiblePoint(Point nextPoint, Point previousPoint,
      PathGroup pathGroup) {
    return (nextPoint.getX() >= 0 && nextPoint.getX() < grid[0].length
        && nextPoint.getY() >= 0 && nextPoint.getY() < grid.length
        && grid[nextPoint.getY()][nextPoint.getX()] != -1
        && !nextPoint.equalsTo(previousPoint)
        && !pathGroup.isPointVisited(nextPoint));
  }
  
    
  
  public ArrayList<PathGroup> getPathGroupList() {
    if (pathGroupList == null) {
      findPathGroups();
    }
    
    return pathGroupList;
  }

  public void setPathGroupList(ArrayList<PathGroup> pathGroupList) {
    this.pathGroupList = pathGroupList;
  }
  
  public int[][] getGrid() {
	return grid;
  }

  public void setGrid(int[][] pGrid) {
    grid = pGrid;
  }
  
  public void setPointInGrid(Point point, int value) {
    grid[point.getY()][point.getX()] = value;
  }
  
  public int getValueOfPointInGrid(Point point) {
    return grid[point.getY()][point.getX()];
  }
  
  public Point getStartingPoint() {
    return startingPoint;
  }

  public void setStartingPoint(Point startingPoint) {
    this.startingPoint = startingPoint;
  }

}



